__DefBitFieldType BRACond<2>
    NoCond;
    U;
    DIV;
    CONV;

__DefBitFieldType BasePCMode<1>
    REL;
    ABS;

__DefBitFieldType BReg<4>
    B0;
    B1;
    B2;
    B3;
    B4;
    B5;
    B6;
    B7;
    B8;
    B9;
    B10;
    B11;
    B12;
    B13;
    B14;
    B15;

__DefBitFieldType BRClear<1>
    NoClear;
    CLEAR;

__DefGroup BRU: [ALL]
  __Encoding
    field<12,  3> Pred pg = PT;
    field<15,  1> PModi pg.not = False;

  __Description

### BRU相关基础变量和流程

#### 基础变量

**ActivePC**(warp共享，64bit): 当前Warp的活跃PC。

**RPC**(每线程64bit): 当前Warp的线程的GPR-PC的值。

**BReg** (warp共享，每个32bit，每warp 16个)：当前Warp用于Reconvergence的mask。

对于当前活跃状态的线程（`ActiveMask`中的线程），`ActivePC`为真实PC值；非活跃线程的真实PC值为`RPC`。活跃线程的`RPC`可能为任意值。因此，在所有流控制指令前运行`RPC[ActiveMask] = ActivePC`总是合法操作，即使后续部分RPC可能被修改。

#### 内置线程Mask状态变量

内置线程Mask变量是Warp级别的**状态变量**。这些状态变量在同一个Warp上下文内会继承，指令可以修改更新对应状态变量，并且可以被后续指令使用。

**注**：后文中线程在mask**内**或**中**是指mask对应该线程bit为1，在mask**外**则是对应bit为0。

**ValidMask**: `ValidMask`为当前Warp存活线程的mask。

- 初始值：取决于blockDim，对于完整Warp为0xffffffff。不完整Warp则为对应的初始mask。
- 修改方：用户代码中，只有`EXIT`指令能修改`ValidMask`。

`ValidMask`会限定其他Mask的有效范围，理论上只有`ValidMask`中的线程状态才是有意义的，非valid的线程状态会被忽略。

**ActiveMask**: `ActiveMask`为当前Warp活跃线程的mask。`ActiveMask`必须是`ValidMask`的子集。

- 初始值：Warp启动时`ActiveMask=ValidMask`
- 修改方：所有导致线程加入、退出、切换的流控制指令，都可能导致`ActiveMask`发生变化。

**YieldMask**: `YieldMask`为当前处于`YIELD`状态的线程mask。一般认为，处于`Yield`状态的线程需要依赖其他线程的一些可观察结果（比如内存写入）。因此，在分支选择和切换过程中，`Yield`线程具有更低优先级。程序总是倾向于先选择执行非`Yield`线程，从而更容易满足`Yield`的依赖条件。

- 初始值：0
- 修改方：`YIELD`指令会设置`YieldMask`, `BSYNC`指令可能清除`YieldMask`。

**SleepMask**: `SleepMask`为当前处于sleep状态的线程mask。如果当前线程处于Sleep状态，则优先选择其他分支。如果没有其他可选分支，则当前WARP进入休眠状态，IBuffer不会调度运行当前Warp，直到计时器清除对应mask。

- 初始值：0
- 修改方：`NANOSLEEP`会设置`SleepMask`，只有内置计时器会清除`SleepMask`。**注**：由于当前计时器只实现单个计时功能，不能完整记录每个线程的睡眠时间。因而，计时器会以各线程最短的睡眠时间为准，达到指定时间后会一次性清除`SleepMask`中所有bit。

**SwitchMask**: `SwitchMask`是用来做分支选择的辅助mask。

- 初始值：0
- 修改方：`YIELD`指令会设置和清除`SwitchMask`。
- **注**：理论上无论`SwitchMask`初始值是多少，经过几次选择后，所有分支都有机会被选中。因此`SwitchMask`初始值只会影响分支被选择的次序，不会导致死锁。因此可以认为`SwitchMask`初值不影响正确性。不过，为了尽量保证运行行为的可预测性，`SwitchMask`会被所有的Save-restore流程保存和恢复。

#### 指令变量

指令变量是指根据当前指令各输入参数计算得到的变量。与前面Warp级别的状态变量不同，它的值只与当前具体运行的指令有关，并不会传递到后续指令。

**pmask**： 指Guard predicate（`Pg`）及额外`Pp`共同作用后的线程mask。

**membermask**: 指`BSYNC/WARPSYNC`输入的32bit mask，通常用来指定哪些线程需参与同步。对于当前设计，`BSYNC B0;`中的membermask存储在Warp级别公共32bit寄存器`B0`中，用来标记尚未到达同步点的线程mask。`WARPSYNC membermask;`的membermask可能存储在立即数/UGPR/Constan memory/GPR中。当membermask存储在GPR中时，各个线程的membermask可能是分组的，每一组内具有相同值。

**arrivemask**: 用来标定当前已经到达同步点的线程，包括正在运行当前同步指令的变量。

#### 基础流程

基础流程为常用的计算

计算`pmask`: 计算真实的guard predicate的取值，包括`Pg/Pp`两个独立的predicate输入。

```c
pgv = Pg.not ? (!Pg) : Pg;
ppv = Pp.not ? (!Pp) : Pp;    // 大部分指令支持

pmask = array2mask(pgv & ppv & ActiveMask);
```

计算`arrivemask`:

```c
// 只有运行到BSYNC/WARPSYNC时才需要计算arrivemask

// 运行到当前BSYNC/WARPSYNC的线程包括当前Active线程，以及之前已经运行到当前指令的线程（RPC会被赋值为当前ActivePC）
arrivemask = ActiveMask | (ValidMask & array2mask(RPC==ActivePC));
```

在指定mask中选择一组执行：

```c
void switchPCByMask(uint cmask) //
{
    assert( (cmask & ActiveMask)==0);    // cmask必须排除当前Active的线程，如果当前Active线程中存在可继续运行的线程，则必须走其他流程
    assert( (cmask & ValidMask)==cmask); // cmask中必须是valid线程

    uint lid = ffs(cmask); // 在可选线程中选取一个，这里总是选最低位的1

    ActiveMask = cmask & array2mask(RPC==RPC[lid]); // 只选择候选mask内与目标RPC相同的线程
                                                    // TODO: 是否需要考虑cmask外其他相同RPC线程？
    ActivePC = RPC[lid];
}
```

判断候选mask内是否都是sleep线程：

```c
//
cmask = ...;

if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致WARP进入休眠
{
    do_sleep = true;  // 在Warp进入Sleep状态前，需先根据SleepMask清除后的状态设置ActivePC/ActiveMask
}
else{
    do_sleep = false;
    cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
}

//
ActiveMask = ...;
ActivePC = ...;

if (do_sleep) warpSleep();  //
```

Warp状态切换函数：

```c
warpExit();  // 当前Warp完全退出，相关资源释放

warpSleep(); // 当前Warp进入睡眠状态，此期间IBuffer不会调度运行当前Warp，定时结束后会自动解除睡眠状态。
```

### BRA/BRX/CALL/RET

这组指令有特定跳转目标，跳转或不跳转后的`ActiveMask`一定是当前`ActiveMask`的子集，不需要通过`electPC`方式选择PC。

### 同步类PC切换指令 BSYNC/WARPSYNC

同步类PC切换指令包括`BSYNC/WARPSYNC`，两者可以通过输入32bit `membermask`来指定哪些线程应该参与同步。这里`membermask`对`WARPSYNC Ra;`形式可能是各线程有独立的值（但是要满足分组条件），其他均为Warp级别变量。

同步执行结果可以分为以下几种情况：

- 完全同步成功：所有原membermask中线程已到达同步点。对`BSYNC`就是`bmask & ValidMask==0`（`bmask`由于有追加机制，同步点可能不唯一）。对`WARPSYNC`是membermask中所有未退出线程均已运行到当前PC。
- 部分同步成功：只针对`WARPSYNC`。由于`WARPSYNC`存在分组同步模式，此时可能部分分组已完成同步，部分分组尚未完成。此时会分组放行已同步线程，等所有已同步分组放行完成后再通过`electPC`切换至剩余未同步线程。
- 让步放行：只针对`BSYNC`。`BSYNC`在bmask中线程运行`YIELD`后可以豁免该同步点，也即其他非`YIELD`线程不必等待`YIELD`线程到达同步点就继续执行后续指令。
- 同步不成功：无法继续执行后续指令，必须切换至一组未同步线程。

#### BSYNC

参考：[[BSYNC]]

#### WARPSYNC

参考：[[WARPSYNC]]

### 非同步类PC切换指令

非同步类PC切换指令包括`YIELD/EXIT/NANOSLEEP`。

#### YIELD

参考：[[YIELD]]

YIELD的主要操作：

- 分支切换
- 设置YieldMask，使其可以不用参与后续BSYNC同步

这里我们认为YIELD线程通常需要等待其他线程完成特定任务，因而在通常的分支选择中，YIELD线程优先级要比其他线程更低。

**注**：YIELD切换的目标分支没有限制。因此，为了避免死锁和保证分支切换的公平性，需要引入一个Mask保证所有分支都有可能被选到。这里称为`SwitchMask`。只要当前`SwitchMask`存在与`ActiveMask`不同的线程，则一定可以切换到。

#### EXIT

参考：[[EXIT]]

#### NANOSLEEP

参考：[[NANOSLEEP]]

__DefGroup BRU_P: [BRU]
  __Encoding
    field<98, 3> Pred pp = PT;
    field<101, 1> PModi pp.not=False;

// __DefGroup BRU_UP: [BRU]
//   __Encoding
//     field<98,  3> UPred upp = PT;
//     field<101,  1> PModi upp.not=False;

__DefGroup BRU_Imm: [BRU]
  __Encoding
    field<32, 50> SImm50 offset;
  __OperandInfo
    AsmFormat<offset> = CvtIShift(offset, 4);

__DefOptype BRA: [BRU_Imm]
  __Encoding
    field< 0,  8> Optype optype == BRA;

    field<82, 2> BRACond cond = NoCond;

  __Examples

```asm
         BRA `(label);   // target label will be replaced by label offset, should be within current kernel.
         BRA 0x110;
    @P0  BRA 0x800 ;
    @!P1 BRA.U 0x6290 ;
         BRA.DIV 0x3940 ;
         BRA.CONV 0x1730 ;
    @!P1 BRA !P2, 0x7e0 ;   // 额外Pp输入
         BRA.DIV ~URZ, 0x3910 ;
    @!P0 BRA.CONV UR4, 0xbe0 ;
```

  __Semantics
`BRA` 语义与当前Divergence状态有关，具体参考`__Description`章节。

  __Description
`BRA` 是实现Kernel内向固定目标跳转的指令。`BRA`支持额外的跳转判断条件，只有在满足条件时才会发生跳转。如果当前活跃线程的跳转条件不一致，则可能引入新的分歧执行状态。

**注**：汇编中显示的`BRA 0x110;`中的跳转目标`0x110`并不是真实的立即数编码值。真实`offset`编码值为目标地址相对下一条指令地址的偏移，由于PC当前是128bit=16B对齐，因而offset的低4bit不用存（只要PC保证16B对齐，省去低4bit没有问题。后面所有涉及到跳转偏移的计算基本都是这个规律）。如：

```asm
  /*0010*/ BRA `(.Target); // .Target=0x110;
                           // 预期偏移为 .Target - .NextPC =（0x0110 - 0x0020) = 0xf0，实际编码中的offset为0xf，省去低4bit。
                           // 也就是：TargetPC = (ActivePC + 0x10) + offset*0x10，offset=0相当于跳转到下一条指令, offset=-1相当于跳转到自身。
.NextPC:
  /*0020*/ ...
  ...
.Target:
  /*0110*/
```

这是实现相对跳转的必要条件，也就是如果BRA与跳转目标同步移动时，指令编码不用改变（但是反汇编结果会发生变化）。由此会引入的问题是`BRA`的汇编/反汇编会与当前指令地址有关，需要注意。

`BRA`的基础跳转条件为guard predicate（`Pg`）和额外输入的predicate操作数（`Pp`），两者的“与”是最终判断条件:`b=pgv & ppv`(其中`pgv/ppv`是`Pg/Pp`考虑了predicate取反modifier的bool值)。

`BRA`的跳转条件除了输入的`Pg/Pp`外，还可以根据当前Warp的分歧状态来决定是否跳转。判断条件由Modifier `BRACond cond` ([[BRACond]])决定:

- `.NoCond`: 无额外跳转条件
- `.U`:  只有当所有活跃线程均需跳转时才跳转
- `.CONV`: Convergent跳转条件，只有当前Warp不发生Divergence时才跳转；
- `.DIV`: Divergent跳转条件，只有当前Warp会发生Divergence时才跳转；

是否Divergent的判断方法：

- 如果没有UR操作数: 判断当前Active且Guard predicate为真的线程是否是全部Valid线程：

  - `is_div = np.any( pg & activemask ^ validmask)`

- 如果存在UR操作数，则将UR当成一个32bit的线程Mask

  - 判断UR mask中是否有线程valid但不active，如是则为divergent状态；否则：
  - 判断当前Active线程的Guard Predicate值，如果都为True或都为False，则为非divergent状态；否则：
  - 判断是否有UR mask中的线程是active但Guard Predicate为False，如有则是divergent状态

```python
if np.any( (validmask_array^activemask_array) & ur_maskarray):
    is_div = True    # some threads in ur_maskarray are valid but not active
else:
    pv = pgv[activemask_array]

    if not (np.all(pv) or np.all(~pv)): # diverged warp due to guard predicate
        # some threads in ur_maskarray are active but guard predicated off
        is_div = np.any(activemask_array & (~pg_array) & ur_maskarray)
    else:
        is_div = False
```

参考：[bra_div_test](http://10.21.25.1/yangrengcai/isa-testcases/-/tree/main/cases/controlflow/bra_div_test)

```python
def getGoldenBranchCondition(activemask_array, validmask_array, pg_array, bra_modi, ur_maskarray):
    ''' Get golden branch condition

        {@{!}pg}   BRA{ , .U, .DIV, .CONV} `(.target);
        {@{!}pg}   BRA{.DIV, .CONV} URa, `(.target);
        {@{!}pg}   BRA {!}Pp, `(.target);              // Pp is already in pg_array = {!}Pg & {!}Pp

        Arguments:
            activemask_array (ndarray[np.bool_]): current activemask_array
            validmask_array (ndarray[np.bool_]): current validmask_array
            pg_array (ndarray[np.bool_]) : guard predicate value array  (already include extra Pp input)
            bra_modi: branch modifier {'', '.U', '.DIV', '.CONV'}

        Return:
            branch_mask_array (ndarray[np.bool_]): return branch mask for this warp. True for jump, False for fall-through.
    '''

    pgv = pg_array & activemask_array
    if ur_maskarray is None:
        is_div = np.any( pgv ^ validmask_array)

        if bra_modi == '':
            branch_mask_array = pgv
        elif bra_modi == '.U':
            branch_mask_array = np.zeros(32, dtype=np.bool_) # just init for later assignment
            branch_mask_array[activemask_array] = np.all(pgv[activemask_array]) # only jump if all active threads agree to jump
        elif bra_modi == '.DIV':
            if is_div:
                branch_mask_array = pgv
            else:
                branch_mask_array = False & pgv # all false
        elif bra_modi == '.CONV':
            if is_div:
                branch_mask_array = False & pgv # all false
            else:
                branch_mask_array = pgv
        else:
            raise Exception(f'Invalid branch modi "{bra_modi}"!!!')

    else:
        # is_div is True if there are threads in ur_maskarray:
        #   valid but not active, or active but guard predicated off

        ur_mask = array2mask(ur_maskarray)
        vmask = array2mask(validmask_array)
        amask = array2mask(activemask_array)
        valid_not_active = array2mask(validmask_array^activemask_array)
        active_not_pg = array2mask(activemask_array & (~pg_array))
        pg_mask = array2mask(pg_array)

        if np.any( (validmask_array^activemask_array) & ur_maskarray):
            is_div = True    # some threads in ur_maskarray are valid but not active
        else:
            pv = pgv[activemask_array]

            if not (np.all(pv) or np.all(~pv)): # diverged warp due to guard predicate
                # some threads in ur_maskarray are active but guard predicated off
                is_div = np.any(activemask_array & (~pg_array) & ur_maskarray)
            else:
                is_div = False

        branch_mask_array = np.zeros(32, dtype=np.bool_)
        if bra_modi == '.DIV':
            branch_mask_array[activemask_array] = np.any(pgv) & is_div
        elif bra_modi == '.CONV':
            branch_mask_array[activemask_array] = np.any(pgv) & (not is_div)
        else:
            raise Exception(f'Invalid branch modi "{bra_modi}"!!!')

        bmask = array2mask(branch_mask_array)
        print(f'v={vmask:#010x}, a={amask:#010x}, v^a={valid_not_active:#010x}, pg={pg_mask:#010x}, ~pg&a={active_not_pg:#010x}, ur={ur_mask:#010x}, is_div={str(is_div):5s}, bmask={bmask:#010x}')

    return branch_mask_array
```

__DefOpcode BRA_U: [BRA, BRU_P]
  __Encoding
    field< 8,  4> SType stype == U;

    field<24,  6> UReg ura;
    field<84,  1> SignModi ura.bitnot = False;

  __OperandInfo
    InList<pg, ura, pp>;
    OutList<>;
    Order<pg, pp, ura, offset>;
    Bitwidth<ura> = 32;

  __Exception
    EncodingError<IllegalBitFieldValue, "BRA_U should be used with .DIV/.CONV."> = (cond!="DIV") and (cond!="CONV");

__DefOpcode BRA_X: [BRA, BRU_P]
  __Encoding
    field< 8,  4> SType stype == X;
  __OperandInfo
    InList<pg, pp>;
    OutList<>;
    Order<pg, pp, offset>;
//
__DefOptype BRX: [BRU_P]
  __Encoding
    field< 0,  8> Optype optype == BRX;

  __Semantics
```c
TargetPC = ActivePC + INST_SIZE + Vb + offset*INST_SIZE; // Vb may come from GPR/UGPR/ConstantMemory
                                               // TargetPC is 32 x 64bit vector.

pgv = pg.not ? !pg : pg;
ppv = pp.not ? !pp : pp;

b_jump = ActiveMask & pgv & ppv;  // b_jump为32bit mask
if(all(~b_jump))                  // b_jump 全 False，fall through
{
    ActivePC += INST_SIZE;
}
else if(b_jump==ActiveMask) // 活跃线程全部跳转
{
    lid = ffs(ActiveMask);                     // 从当前ActiveMask线程中挑选一个线程
    ActivePC = TargetPC[lid];                  // 将该线程目标设为ActivePC
    ActiveMask &= array2mask(TargetPC == ActivePC);   // 从ActiveMask中去除跳转目标不同的线程
    RPC[TargetPC!=ActivePC] = TargetPC[TargetPC!=ActivePC]; // 将未选中线程的跳转目标存入RPC
}
else // 活跃线程部分跳转，优先执行Fall through分支
{
    ActivePC += INST_SIZE;
    ActiveMask = ~b_jump;            // ActiveMask包括所有不跳转的线程
    RPC[b_jump] = TargetPC[b_jump];  // 将跳转线程的跳转目标存入RPC
}
```

  __Description

`BRX` 是实现Kernel内部间接跳转（Indirect branch）的指令。`BRX`与`BRA`区别在于`BRA`的跳转目标是固定值，而`BRX`可以根据输入GPR的值，跳转到一个或多个目标地址。`BRX`同样可以根据`Pg/Pp`的值发生条件跳转。与`BRA`不同的是，`BRX`不能通过当前的分歧状态判断是否跳转。

`BRX`常用来实现函数指针或跳转表之类的功能。

参考：[[BRA]]

**注**： 这里我们实现的`BRX`功能与NV SM89的`BRX`有些差别。NV较新的`BRX`会使用2个GPR做偏移，但这里我们选择只使用一个GPR做偏移。由于`BRX`主要做kernel内跳转，PC相对偏移超过32bit范围的概率很小。如果确实需要这个功能，可以使用`CALL.REL/RET.REL`，它可以接收2个GPR做相对偏移。NV在具体实现`BRX`的流程中，它的跳转表是32bit，但输入`BRX`时用`SHF.R.S32.HI R7, RZ, 0x1f, R6 ;`这种方式做了符号扩展。所以从使用角度讲，BRX只接收一个GPR做输入是合理的。

参考：[brx_test](http://10.21.25.1/yangrengcai/isa-testcases/-/tree/main/cases/controlflow/brx_test)

```
//--------------------- .nv.constant2.brx_test    --------------------------
	.section	.nv.constant2.brx_test,"a",@"SHT_CUDA_CONSTANT_B2"
	.sectionflags	@""
	.align	4
	.type		brx_test.const_opt.0.16,@"STT_CUDA_OBJECT"
	.size		brx_test.const_opt.0.16,(.L_0 - brx_test.const_opt.0.16)
	.other		brx_test.const_opt.0.16,@"STO_CUDA_CONSTANT STV_INTERNAL"
brx_test.const_opt.0.16:
.nv.constant2.brx_test:
        /*0000*/ 	.word	(.L_x_7 - .text.brx_test)
        /*0004*/ 	.word	(.L_x_8 - .text.brx_test)
        /*0008*/ 	.word	(.L_x_9 - .text.brx_test)
        /*000c*/ 	.word	(.L_x_10 - .text.brx_test)
.L_0:

//--------------------- .text.brx_test            --------------------------
	.section	.text.brx_test,"ax",@progbits
	.sectioninfo	@"SHI_REGISTERS=24"
	.align	128
        .global         brx_test
        .type           brx_test,@function
        .size           brx_test,(.L_x_15 - brx_test)
brx_test:
.text.brx_test:
        /*0000*/                   BSSY B0, `(.L_x_0) ;
        /*0010*/                   IMAD.SHL.U32 R6, R6, 0x4, RZ ;
        /*0020*/                   BSSY B1, `(.L_x_1) ;
        /*0030*/                   IADD3 R0, R6, 0x8000, RZ ;
        /*0040*/                   LDC R6, c[R0+`((brx_test.const_opt.0.16 + -0x8000))] ;        // 读取32bit跳转表Entry到R6
        /*0050*/                   SHF.R.S32.HI R7, RZ, 0x1f, R6 ;                               // 将R6符号扩展到R7，从而R[6:7]为S64类型
.L_x_6:
        /*0060*/                   BRX R6 `(((.text.brx_test - .) - 0x10))               (*"BRANCH_TARGETS .L_x_7,.L_x_8,.L_x_9,.L_x_10"*);
                                   // 这里 `.text.brx_test`是当前kernel的基地址，`.`表示当前PC，`-0x10`表示相对下一条指令
                                   // 所以这里的汇编结果就是：BRX R6 -0x70 (offset=-0x7,低4bit省去); 相当于是BRX下一条指令地址取负数
.L_x_7:
        /*0070*/                   IADD3 R4, R4, 0x1, RZ ;
        /*0080*/                   BRA `(.L_x_3) ;
.L_x_8:
        /*0090*/                   SHF.L.U32 R4, R4, 0x1, RZ ;
        /*00a0*/                   BRA `(.L_x_3) ;
.L_x_9:
        /*00b0*/                   IADD3 R4, R4, -0x3, RZ ;
.L_x_3:
        /*00c0*/                   BSYNC B1 ;
.L_x_1:
        /*00d0*/                   BRA `(.L_x_4) ;
.L_x_10:
        /*00e0*/                   EXIT ;
.L_x_4:
        /*00f0*/                   BSYNC B0 ;
.L_x_0:
        /*0100*/                   RET.ABS.NODEC R20 0x0 ;
.L_x_5:
        /*0110*/                   BRA `(.L_x_5);
```

这里简单描述`BRX`的参考汇编流程（TODO：具体实现方式待定）。这里`BRX`可能会跳转到`Label[3]=[.Next, .L1, .L2]`三个潜在目标，其中跳转表内容为：`Coff[3] = [.Next-.Base, .L1-.Base, .L2-.Base]`。

```assembly
.text.k:   // 当前编译单元的运行时基地址，这里也称为BasePC
.Base:     // 当前编译单元的基地址，可以是Kernel或Function的入口。通常这是相对地址。独立编译单元的.Base通常为0, 但如果有inline之类操作，.Base可能会改成函数内联处的相对偏移。
  ...
.Current:  // 当前BRX所在指令
  BRX R6, `(.Base - .Next);   (*"BRANCH_TARGETS .Next, .L1, .L2"*);
.Next:     // BRX下一条指令，通常也是第0个跳转表入口
  ...
.L1:       // 第1个跳转表入口
  ...
.L2:       // 第2个跳转表入口
  ...
```

那跳转到第i个入口的方式为：```BRX Coff[i], `(.Base - .Next);```，此时:

```
TargetPC = ActivePC + INST_SIZE + Va + offset*INST_SIZE                          // ActivePC = BasePC + .Current
         = BasePC + .Current + INST_SIZE + Coff[i] + (.Base - .Next)        // Coff[i] = L[i] - .Base
         = BasePC + .Current + INST_SIZE + (Label[i] - .Base) + (.Base - .Next) // .Current + 0x10  = .Next
         = BasePC + Label[i]
```

正好就是跳转表对应入口的实际PC值。

  __Examples

```asm
BRX R6, `(.Base - . - 0x10);  // fixup形式，`.Base`为BRX目标的相对基地址，相当于跳转表项为[.L1-.Base, .L2-.Base, .L3-.Base,...]
                              // .Base一般是编译期决定的，但是可能有链接时做relocation的可能，此时因为有额外offset，需定义相关的.rela类型
                              // '.' 表示当前指令地址，相当于是一个隐式的label
BRX R6, -0x70;    // NV当前可能写作BRX R6 -0x70;
                  // 这里为了简化实现，我们强制汇编形式中只有内存地址表达式`[R#+Imm]`这种可以操作数连写，其他情况操作数必须用","分隔开。

@P0 BRX R5, -0x110;
@P0 BRX !P1, R5, -0x110;
@P0 BRX UR4, -0x110;
    BRX c[0x3][0x180];
```

__DefOpcode BRX_R: [BRX, BRU_Imm]
  __Encoding
    field< 8,  4> SType stype == R;
    field<24,  8> Reg ra;
  __OperandInfo
    InList<pg, pp, ra>;
    OutList<>;
    Order<pg, pp, ra, offset>;
    Bitwidth<ra> = 32;

__DefOpcode BRX_U: [BRX, BRU_Imm]
  __Encoding
    field< 8,  4> SType stype == U;
    field<24,  6> UReg ura;
  __OperandInfo
    InList<pg, pp, ura>;
    OutList<>;
    Order<pg, pp, ura, offset>;
    Bitwidth<ura> = 32;

__DefOpcode BRX_C: [BRX]
  __Encoding
    field< 8,  4> SType stype == C;

    field<32, 22> CMem vb;
  __OperandInfo

    InList<pg, pp>;
    OutList<>;
    Order<pg, pp, vb>;
    Bitwidth<vb> = 32;

__DefGroup BRU_Base
  __Encoding
    field<82,  1> BasePCMode mode;

__DefOptype CALL: [BRU_P, BRU_Base]
  __Encoding
    field< 0,  8> Optype optype == CALL;

  __Semantics

```c
if(mode==ABS)
  TargetPC = Va + offset*INST_SIZE;
else
  TargetPC = ActivePC + INST_SIZE + Va + offset*INST_SIZE;
```

`CALL/RET`只有目标PC的计算方式与`BRX`不同，跳转行为本身是一样的。跳转行为的语义参考[[BRX]].

  __Description

与`BRX`类似，`CALL/RET`也是实现间接跳转的指令。不同点是，`CALL/RET`不再使用类似`BRX`的编译期确定的constant memory跳转表，而是使用运行期`symbol`信息来定义跳转目标（因而也需要64bit信息来指定跳转目标）。`CALL/RET`的跳转范围可以是任意可执行代码，比`BRX`更灵活。同时，`CALL/RET`支持指定绝对位置跳转和相对位置跳转。

**注**： 当前`CALL/RET`指令本身不会额外操作local栈，也不会记录深度信息（[参考](http://10.21.25.1/yangrengcai/isa-testcases/-/tree/main/cases/controlflow/depth_test)），因而`CALL/RET`在硬件实现上没有区别。但是为了软件实现和分析的方便，这里仍然独立为两个不同的操作。

`CALL/RET`在很多情况下会与`LEPC`联用（参考:[[LEPC]]），其中`LEPC`可以获得当前PC信息，而`CALL/RET`可以根据`LEPC`返回值或其表达式决定具体跳转目标。通常说来，这适用于同一段代码的`CALL/RET`会从不同入口调用的情况，此时`CALL/RET`的输入值在不同入口是不一样的，这样才能根据输入值返回到合适的地方。如：

```
.Entry1: // Entry 1
  LEPC R[8:9];
  ...                     // update R[8:9] to .Ret1 : R[8:9] += .Ret1 - .Entry1
  CALL.ABS R[6:7], 0x0;   // jump to .Func
.Ret1:
  ...                     // return point for entry 1

.Entry2: // Entry 2
  LEPC R[8:9];
  ...                     // update R[8:9] to .Ret2 : R[8:9] += .Ret2 - .Entry2
  CALL.ABS R[6:7], 0x0;   // jump to .Func
.Ret2:
  ...                     // return point for entry 2

  // Function body
.Func:
  ...
  RET.ABS R[8:9], 0x0;     // return to .Ret1 or .Ret2, depending on caller
```

**注2**：`CALL/RET`由于可能引入运行期变量的地址信息，可能会作为Relocation的目标指令，具体类型和处理方式需在ELF处理程序中约定。由于offset没有存储PC的低4bit，做relocation填充时要注意修正。

  __Examples
```
      CALL.ABS `(.Target);      // ABS模式时.Target 必须是个symbol
      CALL.ABS 0x0;             // 等价于： CALL.ABS RZ, 0x0; 通常这是个Relocation入口，程序装载时0x0会被替换为对应Symbol的地址。
      CALL.REL `(.Label);       // REL模式时.Label可以是label？此时相当于BRA/BRX。

  @P1 CALL.ABS R[8:9], 0x110;   // 注：与BRX类似，这里同样要求GPR部分与立即数部分用","分开
  @P1 CALL.ABS P0, R[8:9], 0x110;

      CALL.ABS UR[4:5];         //
      CALL.REL c[0x2][0x100];   // 注意地址对齐，读取64bit constant memory
```

__DefOpcode CALL_R: [CALL, BRU_Imm]
  __Encoding
    field< 8,  4> SType stype == R;
    field<24,  8> Reg ra;
  __OperandInfo
    InList<pg, pp, ra>;
    OutList<>;
    Order<pg, pp, ra, offset>;
    Bitwidth<ra> = 64;

__DefOpcode CALL_U: [CALL, BRU_Imm]
  __Encoding
    field< 8,  4> SType stype == U;
    field<24,  6> UReg ura;
  __OperandInfo
    InList<pg, pp, ura>;
    OutList<>;
    Order<pg, pp, ura, offset>;
    Bitwidth<ura> = 64;

__DefOpcode CALL_C: [CALL]
  __Encoding
    field< 8,  4> SType stype == C;

    field<32, 22> CMem vb;
  __OperandInfo

    InList<pg, pp, vb>;
    OutList<>;
    Order<pg, pp, vb>;
    Bitwidth<vb> = 64;

  __Exception
    // TODO: coffset alignment to 64bit?

__DefOptype RET: [BRU_P, BRU_Base]
  __Encoding
    field< 0,  8> Optype optype == RET;

  __Semantics

参考：[[CALL]]

  __Description
当前`RET`的硬件行为与`CALL`完全一致。参考：[[CALL]]。

__DefOpcode RET_R: [RET, BRU_Imm]
  __Encoding
    field< 8,  4> SType stype == R;
    field<24,  8> Reg ra;
  __OperandInfo
    InList<pg, pp, ra>;
    OutList<>;
    Order<pg, pp, ra, offset>;
    Bitwidth<ra> = 64;

__DefOpcode RET_U: [RET, BRU_Imm]
  __Encoding
    field< 8,  4> SType stype == U;
    field<24,  6> UReg ura;
  __OperandInfo
    InList<pg, pp, ura>;
    OutList<>;
    Order<pg, pp, ura, offset>;
    Bitwidth<ura> = 64;

__DefOpcode RET_C: [RET]
  __Encoding
    field< 8,  4> SType stype == C;

    field<32, 22> CMem vb;
  __OperandInfo

    InList<pg, pp, vb>;
    OutList<>;
    Order<pg, pp, vb>;
    Bitwidth<vb> = 64;

__DefOptype LEPC: [BRU]
  __Encoding
    field< 0,  8> Optype optype == LEPC;
    field<16,  8> Reg rd;

  __Semantics

```c
pmask = ActiveMask & pgv;
rd[mask2array(pmask)] = ActivePC;
```

  __Examples

```
  LEPC R[4:5];   // default offset=0x0
  LEPC R[4:5], 0x80;
```

__DefOpcode LEPC_I: [LEPC]
  __Encoding
    field< 8,  4> SType stype == I;
    field<32, 50> SImm50 offset=0x0;
  __OperandInfo
    InList<pg>;
    OutList<rd>;
    Order<pg, rd, offset>;
    Bitwidth<rd> = 64;
    AsmFormat<offset> = CvtIShift(offset, 4);

__DefOptype EXIT: [BRU_P]
  __Encoding
    field< 0,  8> Optype optype == EXIT;

  __Semantics

```c
// pmask表示guard pred为true的线程，~pmask的线程将fall through到下一个指令
pmask = array2mask(pgv & ppv & ActiveMask);

mask_np = ActiveMask & ~pmask;    // 当前活跃线程（显然已到达同步点）中guard pred为false的线程
mask_p  = ActiveMask &  pmask;

// 判断是否有pmask为false的线程，如果有，则当前指令相当于NOP，fall through到下一个指令
// 这里也包括部分退出的情况
if (mask_np) // TODO:
{
    ValidMask = ValidMask & ~pmask;   //

    ActiveMask = mask_np;
    ActivePC += INST_SIZE;
    return;
}
else // 当前Active线程全部退出
{
    ValidMask = ValidMask & ~ActiveMask;   //

    if (ValidMask == 0) // 所有Valid线程已退出，当前Warp退出
    {
        warpExit();
    }
    else{  // 当前虽然所有Active线程退出，但存在其他Valid线程，切换到其他Valid分支

        // EXIT的切换候选为所有Valid线程
        cmask = ValidMask;
        if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致WARP进入休眠
        {
            do_sleep = true;  // 在Warp进入Sleep状态前，需先根据SleepMask清除后的状态设置ActivePC/ActiveMask
        }
        else{
            do_sleep = false;
            cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
        }

        mask_ny = cmask & ~YieldMask; // 优先跳转到当前ValidMask中不处于Yield状态的线程
        mask_y  = cmask &  YieldMask; //

        if(mask_ny)
            lid = ffs(mask_ny);
        else
            lid = ffs(mask_y);

        ActivePC = RPC[lid];
        ActiveMask = array2mask(RPC==RPC[lid]) & ValidMask;

        if (do_sleep) warpSleep();
    }
}
```

  __Description

`EXIT`指令是退出当前活跃线程的指令，它会结束当前线程的运行。如果当前Warp的所有线程都已退出，则Warp会完全退出，并释放相应资源。如果仍有其他线程存活，当前Warp会切换到对应分支继续执行。

  __Examples
```
     EXIT;
@P0  EXIT;
@!P1 EXIT, P2;
```

__DefOpcode EXIT_X: [EXIT]
  __Encoding
    field< 8,  4> SType stype == X;
  __OperandInfo
    InList<pg, pp>;
    OutList<>;
    Order<pg, pp>;

__DefOptype BMOV: [BRU]
  __Encoding
    field< 0,  8> Optype optype == BMOV;

  __Semantics

```c
pmask = ActiveMask & pgv & ppv;

if(stype==X) // Mov BReg to Reg
    rd[pmask] = breg;
else         // Mov Reg to BReg
    if(pmask!=0)
        breg = ra[ffs(pmask)];   // BReg是scalar，因而选取第一个活跃线程的ra

if(clear==CLEAR && pmask!=0)  // Only for BMOV_B
    breg = 0x0;
```

  __Description

`BMOV`是实现B寄存器保存与恢复的指令。

  __Examples
```
   BMOV B0, R3;
   BMOV R5, B11;
   BMOV.CLEAR R6, B0;
```

__DefOpcode BMOV_X: [BMOV]
  __Encoding
    field< 8,  4> SType stype == X;
    field<16,  8> Reg rd;
    field<84,  4> BReg breg;
    field<82,  1> BRClear clear=NoClear;
  __OperandInfo
    InList<pg, breg>;
    OutList<rd, breg>;  // in + out for breg
    Order<pg, rd, breg>;
    Bitwidth<rd> = 32;
    Bitwidth<breg> = 32;

__DefOpcode BMOV_R: [BMOV]
  __Encoding
    field< 8,  4> SType stype == R;
    field<84,  4> BReg breg;
    field<24,  8> Reg ra;
  __OperandInfo
    InList<pg, ra>;
    OutList<breg>;  // in + out for breg
    Order<pg, breg, ra>;
    Bitwidth<ra> = 32;
    Bitwidth<breg> = 32;

__DefOptype BSSY: [BRU]
  __Encoding
    field< 0,   8> Optype optype == BSSY;
  __Semantics

```c
pmask = ActiveMask & pgv;
if(pmask != 0) breg |= pmask;  // BSSY总是往breg中追加当前pmask，因而在Warp初始时，应将所有Breg初始化为0。
```

  __Examples
```
     BSSY B0;
@P0  BSSY B15;
```

__DefOpcode BSSY_I: [BSSY]
  __Encoding
    field< 8,  4> SType stype == I;
    field<84,  4> BReg breg;
    field<32, 32> SImm32 syncpoint;
  __OperandInfo
    InList<pg, breg>;
    OutList<breg>;  // in + out for breg
    Order<pg, breg, syncpoint>;
    Bitwidth<breg> = 32;
    AsmFormat<syncpoint> = CvtIShift(syncpoint, 4);

__DefOptype BSYNC: [BRU]
  __Encoding
    field< 0,  8> Optype optype == BSYNC;
  __Semantics

```c
// BSYNC bmask;  // bmask为32bit B寄存器的值
bmask = bmask & ValidMask; // 去除所有退出线程

// pmask表示guard pred为true的线程，~pmask的线程将fall through到下一个指令
pmask = array2mask(pgv & ppv & ActiveMask);

// 已到达同步点的线程mask，包括当前ActiveMask，以及之前已到达“当前”BSYNC指令的所有Valid线程
arrivemask = ActiveMask | (ValidMask & array2mask(RPC==ActivePC));

mask_np = ActiveMask & ~pmask;      // 当前活跃线程（显然已到达同步点）中guard pred为false的线程
mask_p  = ActiveMask &  pmask;

// 判断是否有pmask为false的线程，如果有，则当前指令相当于NOP，fall through到下一个指令
if (mask_np)
{
    RPC[mask2array(mask_p)] = ActivePC;
    ActiveMask = mask_np;
    ActivePC += INST_SIZE;
    return;
}
else{
    // 如果对所有Active线程的pmask都为true，则执行BSYNC流程。
    // 注意：由于之前pmask=false的分支已经先执行，等回过来执行当前pmask=true分支时，ActiveMask已经发生变化，相当于此时pmask==ActiveMask

    // 将bmask中对应ActiveMask的bit清除
    bmask = bmask & ( bmask ^ ActiveMask);

    // 首先判断是否已完成同步，如果完成同步，则运行下一条指令
    // 注：该判断同样适用于当前没有发生Divergence的情况
    if (bmask==0)  // 完全同步成功
    {
        ActiveMask = arrivemask; // 新ActiveMask
        ActivePC += INST_SIZE;
        YieldMask = YieldMask ^ (YieldMask & ActiveMask);  // 清除当前YieldMask中已被同步的线程
        return;
    }
    else    // 未能完全同步成功
    {
        // 可选的切换目标：当前未到达线程中的非Yield线程
        cmask = ValidMask & ~arrivemask & ~YieldMask;
        if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致BSYNC让步放行
        {
            //
            ActiveMask = arrivemask;    // arrivemask中必然不包括SleepMask中的线程
            ActivePC += INST_SIZE;
            YieldMask = YieldMask ^ (YieldMask & pmask);  // 清除当前YieldMask中已被同步的线程
            return;
        }
        else{
            cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
        }

        mask_ny_b  = cmask &  bmask; // 未到达线程中，在YieldMask**外**且bmask**内**的线程
        mask_ny_nb = cmask & ~bmask; // 未到达线程中，在YieldMask**外**且bmask**外**的线程

        RPC[ActiveMask] = ActivePC;

        // 如果BSYNC没有完全同步成功，则又存在两种情况：同步不成功/让步放行
        if (mask_ny_b) // 同步不完全成功，切换到需同步但未到达的非yield分支
        {
            switchPCByMask(mask_ny_b);
            return;
        }
        else if (mask_ny_nb) // 同步不完全成功，但可以让步放行；先切换到不需同步的非Yield线程，总是将Yield状态线程的优先级放到非Yield线程之后
        {
            switchPCByMask(mask_ny_nb);
            return;
        }
        else // 同步不完全成功，且ValidMask中已经没有非Yield线程，则**让步放行**当前BSYNC
        {
            ActiveMask = arrivemask;
            ActivePC += INST_SIZE;
            YieldMask = YieldMask ^ (YieldMask & pmask);  // 清除当前YieldMask中已被同步的线程, TODO:???
            return;
        }
    }
}
```

  __Examples
```
     BSYNC B0;
@!P2 BSYNC B0;
```

__DefOpcode BSYNC_X: [BSYNC]
  __Encoding
    field< 8,  4> SType stype == X;
    field<84,  4> BReg breg;
  __OperandInfo
    InList<pg, breg>;
    OutList<breg>;  // in + out for breg
    Order<pg, breg>;
    Bitwidth<breg> = 32;

__DefOptype YIELD: [BRU_P]
  __Encoding
    field< 0,  8> Optype optype == YIELD;
  __Semantics

```c
// pmask表示guard pred为true的线程，~pmask的线程将fall through到下一个指令
pmask = array2mask(pgv & ppv & ActiveMask);

mask_np = ActiveMask & ~pmask;    // 当前活跃线程（显然已到达同步点）中guard pred为false的线程
mask_p  = ActiveMask &  pmask;

// 判断是否有pmask为false的线程，如果有，则当前指令相当于NOP，fall through到下一个指令
if (mask_np) // TODO:
{
    RPC[mask2array(mask_p)] = ActivePC;
    ActiveMask = mask_np;
    ActivePC += INST_SIZE;
    return;
}
else
{
    if (ValidMask ^ ActiveMask == 0) // No divergence here, same as nop
    {
        ActivePC += INST_SIZE;
    }
    else{
        SwitchMask = SwitchMask & (ValidMask ^ ActiveMask); // 只保留当前SwitchMask中Valid但不Active的部分
        if (SwitchMask==0)                                  // 如果当前SwitchMask已被清空，则重置为所有Valid但不Active的部分
            SwitchMask = ValidMask ^ ActiveMask;

        // YIELD只能在当前SwitchMask中挑选线程，否则可能导致在部分分支间来回跳转导致死锁
        cmask = SwitchMask;

        if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致WARP进入休眠
        {
            do_sleep = true;  // 在Warp进入Sleep状态前，需先根据SleepMask清除后的状态设置ActivePC/ActiveMask
        }
        else{
            do_sleep = false;
            cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
        }

        mask_ny = cmask & ~YieldMask; // 优先跳转到当前SwitchMask中不处于Yield状态的线程
        mask_y  = cmask &  YieldMask; //

        if(mask_ny)
            lid = ffs(mask_ny);
        else
            lid = ffs(mask_y);

        YieldMask |= ActiveMask;

        ActivePC = RPC[lid];
        ActiveMask = array2mask(RPC==RPC[lid]) & (ValidMask ^ ActiveMask);

        // TODO: SwitchMask只在YIELD中使用，因为理论上只有多个分支相互YIELD切换才需要SwitchMask
        // 但，其他指令是否可以更新SwitchMask？
        SwitchMask = SwitchMask & (ValidMask ^ ActiveMask); // 更新SwitchMask，只保留当前SwitchMask中Valid但不Active的部分
        if (SwitchMask==0)                                  // 如果当前SwitchMask已被清空，则重置为所有Valid但不Active的部分
            SwitchMask = ValidMask ^ ActiveMask;

        if(do_sleep) warpSleep();
    }
}
```

  __Examples

```
     YIELD;
@P0  YIELD;
@P0  YIELD !P3;
```

__DefOpcode YIELD_X: [YIELD]
  __Encoding
    field< 8,  4> SType stype == X;
  __OperandInfo
    InList<pg, pp>;
    OutList<>;
    Order<pg, pp>;

__DefOptype BREAK: [BRU_P]
  __Encoding
    field< 0,  8> Optype optype == BREAK;

  __Semantics

```c
pmask = ActiveMask & pgv & ppv;
breg = breg ^ (breg & pmask);     // 把BReg中对应bit清除
```

__DefOpcode BREAK_X: [BREAK]
  __Encoding
    field< 8,  4> SType stype == X;
    field<84,  4> BReg breg;
  __OperandInfo
    InList<pg, breg, pp>;
    OutList<breg>;  // in + out for breg
    Order<pg, pp, breg>;
    Bitwidth<breg> = 32;

__DefOptype WARPSYNC: [BRU_P]
  __Encoding
    field< 0,  8> Optype optype == WARPSYNC;

  __Description
`WARPSYNC`是实现Warp内线程同步的指令，它的输入参数为32bit的membermask，用来指定需被同步的线程组合。

参考：[PTX bar.warp.sync](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-bar-warp-sync)

**注**：`bar.warp.sync`有隐式的memory fence语义，由软件负责相关处理。`WARPSYNC`只处理线程指令同步相关。

  __Semantics

```c
// WARPSYNC mmask;
// mmask为32bit membermask, 可能每线程不同，也可以相同（立即数或Constant memory/UR方式操作数）
mmask = mmask & ValidMask; // 去除所有退出线程

// pmask表示guard pred为true的线程，~pmask的线程将fall through到下一个指令
pmask = array2mask(pgv & ppv & ActiveMask);

// 已到达同步点的线程mask，包括当前ActiveMask，以及之前已到达“当前”WARPSYNC指令的所有Valid线程
arrivemask = ActiveMask | (ValidMask & array2mask(RPC==ActivePC));

mask_np = ActiveMask & ~pmask;    // 当前活跃线程（显然已到达同步点）中guard pred为false的线程
mask_p  = ActiveMask &  pmask;

// 判断是否有pmask为false的线程，如果有，则当前指令相当于NOP，fall through到下一个指令
if (mask_np)
{
    RPC[mask2array(mask_p)] = ActivePC;
    ActiveMask = mask_np;
    ActivePC += INST_SIZE;
    return;
}
else{
    // WARPSYNC_II/UR/C对所有线程使用相同的membermask, 当前流程与BSYNC较相似，但不能产生让步放行
    // WARPSYNC mmask;
    if( stype != "R")
    {
        // 首先判断是否已完成同步，如果完成同步，则运行下一条指令
        // 注：该判断同样适用于当前没有发生Divergence的情况
        if ( (arrivemask & mmask) ^ mmask == 0)  // membermask的线程均在arrivemask中，同步成功
        {
            ActiveMask = mmask;    // 注 ：WARPSYNC不能放行mmask外的指令！TODO：BSYNC不受此限制？
                                   // 注2：WARPSYNC正确性要求原ActiveMask必须在membermask中，因而此时原ActiveMask都被放行，不用更新GPR-PC
                                   // 注3：mmask中的非valid线程已去除，因而不用担心退出线程复活
            ActivePC += INST_SIZE;
            return;
        }
        else // 未同步成功
        {
            // 所有需同步但未到达的线程
            cmask = ValidMask & ~arrivemask & mmask;
            if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致WARP进入休眠
            {
                do_sleep = true;  // 在Warp进入Sleep状态前，需先根据SleepMask清除后的状态设置ActivePC/ActiveMask
            }
            else{
                do_sleep = false;
                cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
            }

            // 将未到达线程分成以下互不重叠的2组，如果未同步成功则以下2组必然有一组不全为0
            mask_m_ny  = cmask & ~YieldMask; // 需同步但未到达线程中的非yield分支
            mask_m_y   = cmask &  YieldMask; // 需同步但未到达线程中的yield分支

            RPC[ActiveMask] = ActivePC;

            if (mask_m_ny) // 切换到需同步但未到达的非yield分支
            {
                switchPCByMask(mask_m_ny);
                if(do_sleep) warpSleep();
                return;
            }
            else // 切换到需同步但未到达的yield分支
            {
                switchPCByMask(mask_m_y);
                if(do_sleep) warpSleep();
                return;
            }
        }
    }
    else{ // WARPSYNC_R 各线程的membermask需分组

        // 首先做合法性判断
        // 1. 当前线程必须在membermask中
        assert(lanemask & mmask != 0);
        // 2. 同组各线程的membermask必须完全相同(How?)
        // TODO:

        // cmask表示当前线程分组的未到达线程，每线程独立，cmask==0表示当前线程所在分组已完成同步
        thd_syncmask = mmask ^ (mmask & arrivemask);

        warp_syncmask = ValidMask & array2mask(thd_syncmask==0); // 当前已完成同步的线程mask

        // 首先判断是否存在线程已完成同步，如果完成同步，则需运行下一条指令
        if (warp_syncmask!=0)
        {
            // 分组放行，每次只放行一组，等同于NV的WARPSYNC.EXCLUSIVE
            lid = ffs(sync_mask);

            amask = mmask[lid];  // mmask中已去除非valid线程
            RPC[mask2array(ActiveMask^(ActiveMask & amask))] = ActivePC; // 更新未放行线程的GPR PC
            ActivePC += INST_SIZE;
            ActiveMask = amask;
            //
        }
        else{
            // 如果没有分组完成同步，需从mmask中未到达的valid线程中选取一组
            cmask = array_or(mmask) ^ arrivemask; // array_or(mmask)为所有 membermask的or, 表示所有需参与同步的线程
            if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致WARP进入休眠
            {
                do_sleep = true;  // 在Warp进入Sleep状态前，需先根据SleepMask清除后的状态设置ActivePC/ActiveMask
            }
            else{
                do_sleep = false;
                cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
            }

            mask_ny = cmask & ~YieldMask;       // 需同步但未到达的非Yield线程
            mask_y  = cmask &  YieldMask;       // 需同步但未到达的Yield线程

            if (mask_ny)
                lid = ffs(mask_ny);
            else
                lid = ffs(mask_y);

            RPC[mask2array(ActiveMask)] = ActivePC;   // 更新RPC

            ActivePC = RPC[lid];
            ActiveMask = array2mask(RPC==RPC[lid]) & ValidMask;

            if (do_sleep) warpSleep();
        }
    }
}
```

  __Examples
```
     WARPSYNC 0xffffffff;
@P0  WARPSYNC R9;
@P1  WARPSYNC P3, UR2;
     WARPSYNC c[0x2][0x100];
```

__DefOpcode WARPSYNC_R: [WARPSYNC]
  __Encoding
    field< 8,  4> SType stype == R;

    field<32,  8> Reg rb;
    field<97,  1> SignModi rb.bitnot=False;
  __OperandInfo
    InList<pg, rb, pp>;
    OutList<>;
    Order<pg, pp, rb>;
    Bitwidth<rb> = 32;

__DefOpcode WARPSYNC_U: [WARPSYNC]
  __Encoding
    field< 8,  4> SType stype == U;

    field<32,  6> UReg urb;
    field<97,  1> SignModi urb.bitnot=False;
  __OperandInfo
    InList<pg, urb, pp>;
    OutList<>;
    Order<pg, pp, urb>;
    Bitwidth<urb> = 32;

__DefOpcode WARPSYNC_I: [WARPSYNC]
  __Encoding
    field< 8,  4> SType stype == I;

    field<32, 32> UImm32 membermask;
  __OperandInfo
    InList<pg, pp>;
    OutList<>;
    Order<pg, pp, membermask>;
    Bitwidth<membermask> = 32;

__DefOpcode WARPSYNC_C: [WARPSYNC]
  __Encoding
    field< 8,  4> SType stype == C;

    field<32, 22> CMem vb;

    field<97,  1> SignModi vb.bitnot=False;
  __OperandInfo

    InList<pg, vb, pp>;
    OutList<>;
    Order<pg, pp, vb>;
    Bitwidth<vb> = 32;

__DefOptype NANOSLEEP: [BRU, BRU_P]
  __Encoding
    field< 0,  8> Optype optype == NANOSLEEP;

  __Description

参考：[PTX nanosleep](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#miscellaneous-instructions-nanosleep)

`NANOSLEEP` 使当前线程进入定时睡眠状态，直到计时器自动清除睡眠状态。处于睡眠状态的线程不会被调度运行，WARP在进行分支选择时会优先选择其他分支。如果当前Warp除睡眠分支外没有其他可选分支，则当前Warp会进入睡眠状态，IBuffer会调度运行其他Warp，直到睡眠状态解除。

**注**：由于当前计时器只实现单个计时功能，不能完整记录每个线程的睡眠时间。因而，计时器会以各线程最短的睡眠时间为准，达到指定时间后会一次性清除`SleepMask`中所有bit。

**注2**：NV PTX中sleep的单位为纳秒，实际睡眠时间范围为[0, 2*t]，算是实现上非常灵活的指令。NV可能内部实现只保留了1bit信息，相当于预期睡眠时间是2的幂次，然后通过监控时钟特定bit来触发唤醒。当前芯片目标频率在GHz这个量级，ns和cycle尺度差别不大。所以，单位转换算是可选，用户设置这个数也比较随意，所以这个对用户感知并不明显。

  __Semantics

```c
// pmask表示guard pred为true的线程，~pmask的线程将fall through到下一个指令
pmask = array2mask(pgv & ppv & ActiveMask);

mask_np = ActiveMask & ~pmask;    // 当前活跃线程（显然已到达同步点）中guard pred为false的线程
mask_p  = ActiveMask &  pmask;

// 判断是否有pmask为false的线程，如果有，则当前指令相当于NOP，fall through到下一个指令
if (mask_np) // TODO:
{
    RPC[mask2array(mask_p)] = ActivePC;
    ActiveMask = mask_np;
    ActivePC += INST_SIZE;
    return;
}
else
{
    setTimer(vb * CYCLE_PER_NS);   // 设置定时器，nv的NANOSLEEP 时间单位为ns，注意单位转换

    if (ValidMask ^ ActiveMask == 0) // No divergence here, same as nop
    {
        ActivePC += INST_SIZE;
        warpSleep();   //
    }
    else{
        SwitchMask = SwitchMask & (ValidMask ^ ActiveMask); // 只保留当前SwitchMask中Valid但不Active的部分
        if (SwitchMask==0)                                  // 如果当前SwitchMask已被清空，则重置为所有Valid但不Active的部分
            SwitchMask = ValidMask ^ ActiveMask;

        // YIELD只能在当前SwitchMask中挑选线程，否则可能导致在部分分支间来回跳转导致死锁
        cmask = SwitchMask;

        if( (cmask & SleepMask) ^ SleepMask ==0 ) // 所有可选线程均处于Sleep状态，会导致WARP进入休眠
        {
            do_sleep = true;  // 在Warp进入Sleep状态前，需先根据SleepMask清除后的状态设置ActivePC/ActiveMask
        }
        else{
            do_sleep = false;
            cmask = cmask & ~SleepMask;   // 去除候选mask中处于sleep状态的线程
        }

        mask_ny = cmask & ~YieldMask; // 优先跳转到当前SwitchMask中不处于Yield状态的线程
        mask_y  = cmask &  YieldMask; //

        if(mask_ny)
            lid = ffs(mask_ny);
        else
            lid = ffs(mask_y);

        YieldMask |= ActiveMask;

        ActivePC = RPC[lid];
        ActiveMask = array2mask(RPC==RPC[lid]) & (ValidMask ^ ActiveMask);

        // TODO: SwitchMask只在YIELD中使用，因为理论上只有多个分支相互YIELD切换才需要SwitchMask
        // 但，其他指令是否可以更新SwitchMask？
        SwitchMask = SwitchMask & (ValidMask ^ ActiveMask); // 更新SwitchMask，只保留当前SwitchMask中Valid但不Active的部分
        if (SwitchMask==0)                                  // 如果当前SwitchMask已被清空，则重置为所有Valid但不Active的部分
            SwitchMask = ValidMask ^ ActiveMask;

        if(do_sleep) warpSleep();
    }
}
```

参考：[[YIELD]]

  __Examples

```
      NANOSLEEP 0x100;
      NANOSLEEP R3;
@P0   NANOSLEEP UR4;
@!P1  NANOSLEEP c[0x2][0x120];
```

__DefOpcode NANOSLEEP_R: [NANOSLEEP]
  __Encoding
    field< 8,  4> SType stype == R;
    field<32,  8> Reg rb;
  __OperandInfo
    InList<pg, pp, rb>;
    OutList<>;
    Order<pg, pp, rb>;
    Bitwidth<rb> = 32;

__DefOpcode NANOSLEEP_U: [NANOSLEEP]
  __Encoding
    field< 8,  4> SType stype == U;
    field<32,  6> UReg urb;
  __OperandInfo
    InList<pg, pp, urb>;
    OutList<>;
    Order<pg, pp, urb>;
    Bitwidth<urb> = 32;

__DefOpcode NANOSLEEP_I: [NANOSLEEP]
  __Encoding
    field< 8,  4> SType stype == I;
    field<32, 32> UImm32 vb;
  __OperandInfo
    InList<pg, pp>;
    OutList<>;
    Order<pg, pp, vb>;
    Bitwidth<vb> = 32;

__DefOpcode NANOSLEEP_C: [NANOSLEEP]
  __Encoding
    field< 8,  4> SType stype == C;

    field<32, 22> CMem vb;
  __OperandInfo

    InList<pg, pp>;
    OutList<>;
    Order<pg, pp, vb>;
    Bitwidth<vb> = 32;

__DefOptype RTT : [BRU]
  __Encoding
    field< 0,  8> Optype optype == RTT;

__DefOpcode RTT_X : [RTT]
  __Encoding
    field< 8,  4> SType stype==X;
  __OperandInfo
    InList<pg>;
    OutList<>;
    Order<pg>;

__DefOptype TRAP : [BRU]
  __Encoding
    field< 0,  8> Optype optype == TRAP;

__DefOpcode TRAP_R : [TRAP]
  __Encoding
    field< 8,  4> SType stype==R;
    field<32,  8> Reg rb;
  __OperandInfo
    InList<pg, rb>;
    OutList<>;
    Order<pg, rb>;
    Bitwidth<rb> = 32;

__DefOpcode TRAP_C : [TRAP]
  __Encoding
    field< 8,  4> SType stype==C;
    field<32, 22> CMem vb;
  __OperandInfo
    InList<pg, vb>;
    OutList<>;
    Order<pg, vb>;
    Bitwidth<vb> = 32;

__DefOpcode TRAP_I : [TRAP]
  __Encoding
    field< 8,  4> SType stype==I;
    field<32, 32> UImm32 hidx;
  __OperandInfo
    InList<pg>;
    OutList<>;
    Order<pg, hidx>;
    Bitwidth<hidx> = 32;

__DefOptype SYSCALL : [BRU]
  __Encoding
    field< 0,  8> Optype optype == SYSCALL;

__DefOpcode SYSCALL_X : [SYSCALL]
  __Encoding
    field< 8,  4> SType stype==R;
  __OperandInfo
    InList<pg>;
    OutList<>;
    Order<pg>;

// __DefOpcode SYSCALL_C : [SYSCALL]
//   __Encoding
//     field< 8,  4> SType stype==C;
//     field<32, 22> CMem vb;
//   __OperandInfo
//     InList<pg, vb>;
//     OutList<>;
//     Order<pg, vb>;
//     Bitwidth<vb> = 32;

// __DefOpcode SYSCALL_I : [SYSCALL]
//   __Encoding
//     field< 8,  4> SType stype==I;
//     field<32, 32> UImm32 hidx;
//   __OperandInfo
//     InList<pg>;
//     OutList<>;
//     Order<pg, hidx>;
//     Bitwidth<hidx> = 32;
